package org.springframework.beans.factory.support;

import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.config.*;
import org.springframework.beans.factory.util.StringUtils;
import org.springframework.beans.factory.util.ValueUtils;

import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * @author:刘仁有
 * @desc:
 * @email:953506233@qq.com
 * @data:2019/12/11
 */
public class DefaultListableBeanFactory implements ListableBeanFactory,BeanDefinitionRegistry,BeanPostProcessorRegistry{

    //bd集合
    private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
    //单例池
    private final Map<String,Object> singletonObjects = new ConcurrentHashMap<>(256);
    //早期单例池
    private final Map<String,Object> earlySingletonObjects = new HashMap<>(16);
    //正在创建的单例spring bean的 beanName的集合
    private final Set<String> singletonsCurrentlyInCreation =
            Collections.newSetFromMap(new ConcurrentHashMap<>(16));
    //bd name 集合
    private final List<String> beanDefinitionNames = new ArrayList<>();

    private volatile boolean hasInstantiationAwareBeanPostProcessors;

    private final List<BeanPostProcessor> beanPostProcessors = new CopyOnWriteArrayList<>();

    private String serializationId;

    private static final Map<String, Reference<DefaultListableBeanFactory>> serializableFactories =
            new ConcurrentHashMap<>(8);
    /**
     * 单例池取
     * 取不出，用bd new一个
     * 决策构造器，newInstance()
     * 空对象==暂无属性
     * 加入早期单例池
     * 属性注入
     * 循环依赖问题发现（定义一个正在创建的bean集合）
     * 循环依赖问题（二级缓存解决）
     * 调用init方法
     * 加入单例池，移除早期单例池
     * ioc完毕
     * @param beanName
     * @return
     */
    @Override
    public Object getBean(String beanName) throws Exception {
        Objects.requireNonNull(beanName,"spring bean name must not null at getBean");
        return this.doGetBean(beanName);
    }

    protected Object doGetBean(String name) throws Exception {
        //&XXXX->XXXX
        final String beanName = transformedBeanName(name);

        BeanDefinition bd = beanDefinitionMap.get(beanName);
        Objects.requireNonNull(bd,"NoSuchBeanDefinitionException: No bean named "+beanName);

        Object instance = null;
        //单例直接从单例池拿
        if(bd.isSingleton()){
            Object sharedInstance = getSingleton(beanName);
            if(sharedInstance!=null){
                //正常bean直接返回，factory 判断name是不是&开头，如果是返回factoryBean本身,如果不是返回factoryBean的getObject
                instance = getObjectForBeanInstance(sharedInstance, name, beanName);
                return instance;
            }
            if(singletonsCurrentlyInCreation.contains(beanName)){
                throw new Exception(beanName + " 循环依赖！" + singletonsCurrentlyInCreation);
            }
            singletonsCurrentlyInCreation.add(beanName);
        }

        //决策构造器
        instance = createBeanInstance(beanName,bd);

        if(bd.isSingleton()) {
            earlySingletonObjects.put(beanName, instance);
        }

        //解析属性的@Autowired 和@Value 设置给bd 的PropertyValue 等待注入
        processBeanDefinitionPropertyValue(bd,bd.getBeanClass(),beanName);

        //DI
        populateBean(bd,instance);

        //AOP代理 生命周期回调
        instance = initializeBean(bd,instance);

        //加入单例池
        if(bd.isSingleton()) {
            singletonObjects.put(beanName, instance);
            earlySingletonObjects.remove(beanName);
            singletonsCurrentlyInCreation.remove(beanName);
        }
        return instance;
    }
    //正常bean直接返回，factory 判断name是不是&开头，如果是返回factoryBean本身,如果不是返回factoryBean的getObject
    private Object getObjectForBeanInstance(Object sharedInstance, String name, String beanName) throws Exception {

        if(!(sharedInstance instanceof FactoryBean)||name.startsWith("&")){
            return sharedInstance;
        }
        FactoryBean factoryBean = (FactoryBean) sharedInstance;
        return factoryBean.getObject();
    }

    private String transformedBeanName(String beanName) {
        if(!beanName.startsWith(FACTORY_BEAN_PREFIX)){
            return beanName;
        }
        do {
            beanName = beanName.substring(FACTORY_BEAN_PREFIX.length());
        }
        while (beanName.startsWith(FACTORY_BEAN_PREFIX));
        return beanName;
    }


    private Object getSingleton(String beanName){
        //单例池中拿 第一级缓存
        Object singletonObject = this.singletonObjects.get(beanName);
        //单例池没有&&当前bean正在创建中
        if (singletonObject == null&&isBeanNameInUse(beanName)){
            //从早期单例池拿
            synchronized (this.singletonObjects){
                //early中拿 第二级缓存
                singletonObject = this.earlySingletonObjects.get(beanName);
            }
        }
        return singletonObject;
    }


    private Object initializeBean(BeanDefinition bd, Object instance) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        if(StringUtils.isNotBlank(bd.getInitMethodName())){
            Method method = instance.getClass().getMethod(bd.getInitMethodName(),null);
            method.invoke(instance,null);
        }
        //aop省略
        return instance;
    }

    private void populateBean(BeanDefinition bd, Object instance) throws Exception {
        if(null==instance||null==bd.getPropertyValues()||bd.getPropertyValues().isEmpty())return;
        for(PropertyValue pv:bd.getPropertyValues()){
            if(StringUtils.isBlank(pv.getName())){
                continue;
            }
            Class clazz = instance.getClass();
            Field field = clazz.getDeclaredField(pv.getName());
            field.setAccessible(true);
            Object realVal = ValueUtils.getBaseValue(field.getType(),ValueUtils.getRealValue(this,pv.getValue()));
            field.set(instance,realVal);
        }
    }




    private Object  createBeanInstance(String beanName,BeanDefinition bd) throws Exception {
        //@Bean FactoryMethodName 处理
        if(StringUtils.isNotBlank(bd.getFactoryMethodName())){
            return instantiateUsingFactoryMethod(beanName, bd);
        }
        //推断构造器
        Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(bd.getBeanClass(), beanName);

        //自动装配
        if (ctors != null || bd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
                bd.hasConstructorArgumentValues()) {
            return new ConstructorResolver(this).autowireConstructor(beanName, bd, ctors);
        }
        //使用默认构造器
        return  bd.getBeanClass().newInstance();
    }

    private Object instantiateUsingFactoryMethod(String beanName, BeanDefinition bd) throws Exception {
        //不加 static的 @Bean方法
        if(StringUtils.isNotBlank(bd.getFactoryBeanName())){
            Object obj = getBean(bd.getFactoryBeanName());
            Class<?> clazz = bd.getBeanClass();
            Method method = clazz.getMethod(bd.getFactoryMethodName(),null);
            return method.invoke(obj);
        }
        //静态的@Bean方法
        else{
            Class<?> clazz = bd.getBeanClass();
            Method method = clazz.getMethod(bd.getFactoryMethodName(),null);
            return method.invoke(clazz);
        }
    }


    protected void processBeanDefinitionPropertyValue(BeanDefinition bd, Class<?> beanType, String beanName) {
        for (BeanPostProcessor bp : beanPostProcessors) {
            if (bp instanceof PropertyValueBeanDefinitionPostProcessor) {
                PropertyValueBeanDefinitionPostProcessor pvbdp = (PropertyValueBeanDefinitionPostProcessor) bp;
                pvbdp.postProcessBeanDefinitionPropertyValue(bd, beanType, beanName);
            }
        }
    }

    private Constructor<?>[] determineConstructorsFromBeanPostProcessors(Class<?> beanClass, String beanName) {
        if (beanClass != null && hasInstantiationAwareBeanPostProcessors) {
            for (BeanPostProcessor bp : beanPostProcessors) {
                if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                    SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
                    Constructor<?>[] ctors = ibp.determineCandidateConstructors(beanClass, beanName);
                    if (ctors != null) {
                        return ctors;
                    }
                }
            }
        }
        return null;
    }


    @Override
    public boolean containsBean(String beanName) {
        return false;
    }

    @Override
    public void registerBeanDefinition(String beanName, BeanDefinition bd) {
        Objects.requireNonNull(beanName,"spring bean name must not null at registerBeanDefinition");
        Objects.requireNonNull(bd,"spring bd must not null at registerBeanDefinition");
        Objects.requireNonNull(bd.getBeanClass(),"spring bd class must not null at registerBeanDefinition");
        if(beanDefinitionMap.containsKey(beanName)){
            throw new RuntimeException("beanDefinitions must not contain same beanName:"+beanName);
        }
        beanDefinitionMap.put(beanName,bd);
        beanDefinitionNames.add(beanName);
    }

    @Override
    public void removeBeanDefinition(String beanName) {
        beanDefinitionMap.remove(beanName);
        beanDefinitionNames.remove(beanName);
    }

    //预加载所有的单例非懒加载的bean
    public void preInstantiateSingletons() throws Exception{
        synchronized (beanDefinitionNames){
            for (String beanName : beanDefinitionNames){
                BeanDefinition beanDefinition = this.getBeanDefinition(beanName);
                if(FactoryBean.class.isAssignableFrom(beanDefinition.getBeanClass())){
                    this.doGetBean(FACTORY_BEAN_PREFIX+beanName);
                }else if(beanDefinition.isSingleton()&&!beanDefinition.isLazyInit()){
                    this.doGetBean(beanName);
                }
            }
        }
    }

    @Override
    public void registerBeanPostProcessor(BeanPostProcessor beanPostProcessor){
        Objects.requireNonNull(beanPostProcessor, "BeanPostProcessor must not be null");
        this.beanPostProcessors.remove(beanPostProcessor);
        if (beanPostProcessor instanceof InstantiationAwareBeanPostProcessor) {
            this.hasInstantiationAwareBeanPostProcessors = true;
        }
        this.beanPostProcessors.add(beanPostProcessor);
    }

    public void setSerializationId(String serializationId) {
        if (serializationId != null) {
            serializableFactories.put(serializationId, new WeakReference<>(this));
        }
        else if (this.serializationId != null) {
            serializableFactories.remove(this.serializationId);
        }
        this.serializationId = serializationId;
    }

    @Override
    public int getBeanPostProcessorCount() {
        return beanPostProcessors.size();
    }

    @Override
    public BeanDefinition getBeanDefinition(String beanName) {
        return beanDefinitionMap.get(beanName);
    }

    @Override
    public boolean containsBeanDefinition(String beanName) {
        return beanDefinitionMap.containsKey(beanName);
    }

    @Override
    public String[] getBeanDefinitionNames() {
        return StringUtils.toStringArray(this.beanDefinitionNames);
    }

    @Override
    public int getBeanDefinitionCount() {
        return beanDefinitionNames.size();
    }

    @Override
    public boolean isBeanNameInUse(String beanName) {
        return singletonsCurrentlyInCreation.contains(beanName);
    }

    public void destroySingletons() {
        //回调bean的DestroyMethod
        //TODO
        synchronized (this.singletonObjects) {
            this.singletonObjects.clear();
            this.earlySingletonObjects.clear();
            this.beanPostProcessors.clear();
            this.beanDefinitionMap.clear();
            this.beanDefinitionNames.clear();
            this.hasInstantiationAwareBeanPostProcessors = false;
        }
    }

    @Override
    public String[] getBeanNamesForType(Class<?> type) {
        return getBeanNamesForType(type,true);
    }


//    父类.class.isAssignableFrom(子类.class)
//    子类实例 instanceof 父类类型
    @Override
    public String[] getBeanNamesForType(Class<?> type, boolean includeNonSingletons) {
        Objects.requireNonNull(type,"type must not be null");
        List<String> result = new ArrayList<>();
        for (Map.Entry<String,BeanDefinition> entry : beanDefinitionMap.entrySet()) {
            if(!includeNonSingletons&&entry.getValue().isPrototype()){
                continue;
            }
            if(type.isAssignableFrom(entry.getValue().getBeanClass())){
                result.add(entry.getKey());
            }
        }
        return result.toArray(new String[0]);
    }
}
